package cn.parus.commons.utils;

import cn.parus.commons.common.Execption.CacheSerializationException;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;

/**
 * @Author: Axel
 * @Description: TODO 通用redis方法
 * 支持对象序列化
 **/
@Slf4j
@Component
public class GenericRedisUtil {

    private final ObjectMapper objectMapper;
    private final RedisTemplate redisTemplate;
    private final ParseJsonUtil parseJsonUtil;

    @Autowired
    public GenericRedisUtil(ObjectMapper objectMapper, RedisTemplate redisTemplate, ParseJsonUtil parseJsonUtil) {
        this.objectMapper = objectMapper;
        this.redisTemplate = redisTemplate;
        this.parseJsonUtil = parseJsonUtil;
    }

    // ------redis基础缓存操作---------

    /**
     * 设置缓存（过期时间、过期时间单位）
     * @param key
     * @param value
     * @param timeout
     * @param timeUnit
     * @param <T>
     */
    public <T> void set(String key, T value, long timeout, TimeUnit timeUnit) {
        try {
            String json = objectMapper.writeValueAsString(value);
            redisTemplate.opsForValue().set(key, json, timeout, timeUnit);
        } catch (JsonProcessingException e) {
            throw new CacheSerializationException("对象序列化失败", e);
        }
    }

    /**
     * 获取缓存对象
     * @param key
     * @param clazz
     * @return
     * @param <T>
     */
    public <T> T get(String key, Class<T> clazz) {
        String json = (String) redisTemplate.opsForValue().get(key);
        return parseJsonUtil.parseJson(json, clazz);
    }

    /**
     * 从缓存中获取到集合对象
     * @param key
     * @param elementClazz
     * @return
     * @param <T>
     */
    public <T> List<T> getList(String key, Class<T> elementClazz) {
        String json = (String) redisTemplate.opsForValue().get(key);
        return parseJsonUtil.parseJsonToList(json, elementClazz);
    }

    /**
     * 清除缓存
     * @param key
     */
    public void delete(String key) {
        try {
            redisTemplate.delete(key);
        } catch (Exception e) {
            throw new CacheSerializationException("删除缓存失败: " + key, e);
        }
    }

    // ------Hash操作方法-------

    /**
     * 设置hash字段
     * @param key
     * @param field
     * @param value
     * @param <T>
     */
    public <T> void hset(String key, String field, T value) {
        try {
            String json = objectMapper.writeValueAsString(value);
            redisTemplate.opsForHash().put(key, field, json);
        } catch (JsonProcessingException e) {
            throw new CacheSerializationException("Hash字段序列化失败", e);
        }
    }

    /**
     * 批量设置Hash字段
     * @param key
     * @param filedValueMap
     * @param <T>
     */
    public <T> void hmset(String key, Map<String, T> filedValueMap) {
        Map<String, String> serializedMap = new HashMap<>();
        filedValueMap.forEach((field, value) -> {
            try {
                serializedMap.put(field, objectMapper.writeValueAsString(value));
            } catch (JsonProcessingException e) {
                throw new CacheSerializationException("Hash批量序列化失败", e);
            }
        });
        redisTemplate.opsForHash().putAll(key, serializedMap);
    }

    /**
     * 获取Hash字段对象
     * @param key
     * @param field
     * @param clazz
     * @return
     * @param <T>
     */
    public <T> T hget(String key, String field, Class<T> clazz) {
        String json = (String) redisTemplate.opsForHash().get(key, field);
        return parseJsonUtil.parseJson(json, clazz);
    }

    /**
     * 获取整个Hash
     * @param key
     * @param clazz
     * @return
     * @param <T>
     */
    public <T> Map<String, T> hgetAll(String key, Class<T> clazz) {
        Map<Object, Object> entries = redisTemplate.opsForHash().entries(key);
        Map<String, T> result = new HashMap<>();
        entries.forEach((field, json) -> {
            if (json != null) {
                result.put((String) field, parseJsonUtil.parseJson((String) json, clazz));
            }
        });
        return result;
    }

    /**
     * hash方法方法删除key中对应field的字段
     * @param key
     * @param field
     */
    public void hdelete(String key, String field) {
        try {
            redisTemplate.opsForHash().delete(key, field);
        } catch (Exception e) {
            throw new CacheSerializationException("删除Hash缓存字段失败: " + key + "." + field, e);
        }
    }
}
